﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using NHibernate.Expression.Extensions.Helpers;
using NHibernate.Criterion;

namespace NHibernate.Expression.Extensions {
    public static class ExpressionEx {
        public static SimpleExpression Eq<T, R>(Expression<Func<T, R>> f, R value) {
            return Criterion.Expression.Eq(f.MemberName(), value);
        }

        public static SimpleExpression Eq<T, R>(string prefix, Expression<Func<T, R>> f, R value) {
            return Criterion.Expression.Eq(Prefix(prefix, f.MemberName()), value);
        }

        public static AbstractCriterion EqProperty<T, R>(Expression<Func<T, R>> property, Expression<Func<T, R>> otherProperty) {
            return Criterion.Expression.EqProperty(property.MemberName(), otherProperty.MemberName());
        }

        public static AbstractCriterion EqProperty<T, R>(string prefix, Expression<Func<T, R>> property,
                                                         Expression<Func<T, R>> otherProperty) {
            return Criterion.Expression.EqProperty(Prefix(prefix, property.MemberName()), otherProperty.MemberName());
        }

        public static AbstractCriterion EqProperty<T, R>(Expression<Func<T, R>> property, string prefix,
                                                         Expression<Func<T, R>> otherProperty) {
            return Criterion.Expression.EqProperty(property.MemberName(), Prefix(prefix, otherProperty.MemberName()));
        }

        private static string Prefix(string prefix, string name) {
            return string.Format("{0}.{1}", prefix, name);
        }

        public static AbstractCriterion EqProperty<T, R>(string prefix, Expression<Func<T, R>> property, string otherPrefix,
                                                         Expression<Func<T, R>> otherProperty) {
            return Criterion.Expression.EqProperty(Prefix(prefix, property.MemberName()),
                                         Prefix(otherPrefix, otherProperty.MemberName()));
        }

        public static AbstractCriterion NotEqProperty<T, R>(Expression<Func<T, R>> property,
                                                            Expression<Func<T, R>> otherProperty) {
            return Criterion.Expression.NotEqProperty(property.MemberName(), otherProperty.MemberName());
        }

        public static SimpleExpression Ge<T, R>(Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Ge(property.MemberName(), value);
        }

        public static SimpleExpression Ge<T, R>(string prefix, Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Ge(Prefix(prefix, property.MemberName()), value);
        }

        public static AbstractCriterion GeProperty<T, T2, R>(Expression<Func<T, R>> property, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.GeProperty(property.MemberName(), otherProperty.MemberName());
        }

        public static AbstractCriterion GeProperty<T, T2, R>(string prefix, Expression<Func<T, R>> property, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.GeProperty(Prefix(prefix, property.MemberName()), otherProperty.MemberName());
        }

        public static AbstractCriterion GeProperty<T, T2, R>(Expression<Func<T, R>> property, string prefix, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.GeProperty(property.MemberName(), Prefix(prefix, otherProperty.MemberName()));
        }

        public static AbstractCriterion GeProperty<T, T2, R>(string prefix, Expression<Func<T, R>> property, string otherPrefix, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.GeProperty(Prefix(prefix, property.MemberName()), Prefix(otherPrefix, otherProperty.MemberName()));
        }

        public static SimpleExpression Gt<T, R>(Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Gt(property.MemberName(), value);
        }

        public static SimpleExpression Gt<T, R>(string prefix, Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Gt(Prefix(prefix, property.MemberName()), value);
        }

        public static AbstractCriterion GtProperty<T, T2, R>(Expression<Func<T, R>> property, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.GtProperty(property.MemberName(), otherProperty.MemberName());
        }

        public static SimpleExpression Le<T, R>(Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Le(property.MemberName(), value);
        }

        public static SimpleExpression Le<T, R>(string prefix, Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Le(Prefix(prefix, property.MemberName()), value);
        }

        public static AbstractCriterion LeProperty<T, T2, R>(Expression<Func<T, R>> property, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.LeProperty(property.MemberName(), otherProperty.MemberName());
        }

        public static AbstractCriterion LeProperty<T, T2, R>(string prefix, Expression<Func<T, R>> property, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.LeProperty(Prefix(prefix, property.MemberName()), otherProperty.MemberName());
        }

        public static AbstractCriterion LeProperty<T, T2, R>(Expression<Func<T, R>> property, string prefix, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.LeProperty(property.MemberName(), Prefix(prefix, otherProperty.MemberName()));
        }

        public static AbstractCriterion LeProperty<T, T2, R>(string prefix, Expression<Func<T, R>> property, string otherPrefix, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.LeProperty(Prefix(prefix, property.MemberName()), Prefix(otherPrefix, otherProperty.MemberName()));
        }

        public static SimpleExpression Lt<T, R>(Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Lt(property.MemberName(), value);
        }

        public static SimpleExpression Lt<T, R>(string prefix, Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Lt(Prefix(prefix, property.MemberName()), value);
        }

        public static AbstractCriterion LtProperty<T, R>(Expression<Func<T, R>> property, Expression<Func<T, R>> otherProperty) {
            return Criterion.Expression.LtProperty(property.MemberName(), otherProperty.MemberName());
        }

        public static AbstractCriterion LtProperty<T, T2, R>(string prefix, Expression<Func<T, R>> property, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.LtProperty(Prefix(prefix, property.MemberName()), otherProperty.MemberName());
        }

        public static AbstractCriterion LtProperty<T, T2, R>(Expression<Func<T, R>> property, string prefix, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.LtProperty(property.MemberName(), Prefix(prefix, otherProperty.MemberName()));
        }

        public static AbstractCriterion LtProperty<T, T2, R>(string prefix, Expression<Func<T, R>> property, string otherPrefix, Expression<Func<T2, R>> otherProperty) {
            return Criterion.Expression.LtProperty(Prefix(prefix, property.MemberName()), Prefix(otherPrefix, otherProperty.MemberName()));
        }

        public static AbstractCriterion In<T, R>(Expression<Func<T, R>> property, ICollection<R> values) {
            return Criterion.Expression.InG(property.MemberName(), values);
        }

        public static AbstractCriterion InsensitiveLike<T, R>(Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.InsensitiveLike(property.MemberName(), value);
        }

        public static AbstractCriterion InsensitiveLike<T>(Expression<Func<T, string>> property, string value,
                                                           MatchMode matchMode) {
            return Criterion.Expression.InsensitiveLike(property.MemberName(), value, matchMode);
        }

        public static AbstractCriterion Like<T, R>(Expression<Func<T, R>> property, R value) {
            return Criterion.Expression.Like(property.MemberName(), value);
        }

        public static AbstractCriterion Like<T>(Expression<Func<T, string>> property, string value, MatchMode matchMode) {
            return Criterion.Expression.Like(property.MemberName(), value, matchMode);
        }

        public static AbstractEmptinessExpression IsEmpty<T>(Expression<Func<T, object>> property) {
            return Criterion.Expression.IsEmpty(property.MemberName());
        }

        public static AbstractEmptinessExpression IsNotEmpty<T>(Expression<Func<T, object>> property) {
            return Criterion.Expression.IsNotEmpty(property.MemberName());
        }

        public static AbstractCriterion IsNull<T>(Expression<Func<T, object>> property) {
            return Criterion.Expression.IsNull(property.MemberName());
        }

        public static AbstractCriterion IsNotNull<T>(Expression<Func<T, object>> property) {
            return Criterion.Expression.IsNotNull(property.MemberName());
        }

        public static AbstractCriterion Between<T, R>(Expression<Func<T, R>> property, R lo, R hi) {
            return Criterion.Expression.Between(property.MemberName(), lo, hi);
        }

        public static AbstractCriterion AllEq<T>(IDictionary<Expression<Func<T, object>>, object> propertiesValues) {
            var d = new Hashtable();
            foreach (var kv in propertiesValues)
                d.Add(kv.Key.MemberName(), kv.Value);
            return Criterion.Expression.AllEq(d);
        }
    }
}